1 避免全局变量
//一个全局变量——我们推荐这样使用
var MyApplication = {
name: "Nicholas",
sayName: function(){
alert(this.name);
}
};
2 定义常量,我们推荐这样
var Constants = {
INVALID_VALUE_MSG: "Invalid value!",
INVALID_VALUE_URL: "/errors/invalid.php"
};
function validate(value){
if (!value){
alert(Constants.INVALID_VALUE_MSG);
location.href = Constants.INVALID_VALUE_URL;
}
}
3 注意作用域
这里将document用于局部变量,总是有用的,因为我们不需要每次都去作用域链中寻找我们的
全局变量
function updateUI(){
var doc = document;
var imgs = doc.getElementsByTagName("img");
for (var i=0, len=imgs.length; i < len; i++){
imgs[i].title = doc.title + " image " + i;
}
var msg = doc.getElementById("msg");
msg.innerHTML = "Update complete.";
}
将在一个函数中会用到多次的全局对象存储为局部变量总是没错的。
4 避免属性查找,多次用到一个属性时,应该把它当做一个局部变量使用
一旦多次用到对象属性,应该将其存储在局部变量中。第一次访问该值会是 O(n),然而后续
的访问都会是 O(1),就会节省很多。例如,之前的代码可以如下重写:
var url = window.location.href;
var query = url.substring(url.indexOf("?"));
这个版本的代码只有 4 次属性查找,相对于原始版本节省了 33%。在更大的脚本中进行这种
优化,倾向于获得更多改进。
5 避免双重解释
JavaScript 代码想解析 JavaScript 的时候就会存在双重解释惩罚。当使用 eval()函数
或者是Function构造函数以及使用 setTimeout()传一个字符串参数时都会发生这种情况。
下面有一些例子:
//某些代码求值——避免!!
eval("alert('Hello world!')");
//创建新函数——避免!!
var sayHi = new Function("alert('Hello world!')");
//设置超时——避免!!
setTimeout("alert('Hello world!')", 500);
在以上这些例子中,都要解析包含了 JavaScript 代码的字符串。这个操作是不能在初始的
解析过程中完成的,因为代码是包含在字符串中的,也就是说在 JavaScript 代码运行的同
时必须新启动一个解析器来解析新的代码。
实例化一个新的解析器有不容忽视的开销, 所以这种代码要比直接解 析慢得多。
对于这几个例子都有另外的办法。只有极少的情况下 eval()是绝对必须的,所以尽可能避免
使用。在这个例子中,代码其实可以直接内嵌在 原代码中。对于 Function 构造函数,
完全可以直接写成一般的函数,调用
setTimeout()可以传入函数作为第一个参数。以下是一些例子:
//已修正
alert('Hello world!');
//创建新函数——已修正
var sayHi = function(){
alert('Hello world!');
};
//设置一个超时——已修正
setTimeout(function(){
alert('Hello world!');
}, 500);
如果要提高代码性能,尽可能避免出现需要按照 JavaScript 解释的字符串。
6 优化DOM交互
(1)优化DOM交互
最小化现场更新
一旦你需要访问的 DOM 部分是已经显示的页面的一部分,那么你就是在进行一个现场更新。之所
以叫现场更新,是因为需要立即(现场)对页面对用户的显示进行更新。每一个更改,不管是插入单个
字符,还是移除整个片段,都有一个性能惩罚,因为浏览器要重新计算无数尺寸以进行更新。现场更新
进行得越多,代码完成执行所花的时间就越长;完成一个操作所需的现场更新越少,代码就越快。请看
以下例子:var list = document.getElementById("myList"), item, i; for (i=0; i < 10; i++) { item = document.createElement("li"); list.appendChild(item); item.appendChild(document.createTextNode("Item " + i)); }
这段代码为列表添加了 10 个项目。添加每个项目时,都有 2 个现场更新:一个添加
- 元素,另
一个给它添加文本节点。这样添加 10 个项目,这个操作总共要完成 20 个现场更新。
要修正这个性能瓶颈,需要减少现场更新的数量。一般有 2 种方法。第一种是将列表从页面上移除,
最后进行更新,最后再将列表插回到同样的位置。这个方法不是非常理想,因为在每次页面更新的时候
它会不必要的闪烁。第二个方法是使用文档片段来构建 DOM 结构,接着将其添加到 List 元素中。这
个方式避免了现场更新和页面闪烁问题。请看下面内容:
就是先一次性建好所有的li再放进去。而不是,建一个,放一个。
var list = document.getElementById("myList"),
fragment = document.createDocumentFragment(),
item,
i;
for (i=0; i < 10; i++) {
item = document.createElement("li");
fragment.appendChild(item);
item.appendChild(document.createTextNode("Item " + i));
}
list.appendChild(fragment);
在这个例子中只有一次现场更新,它发生在所有项目都创建好之后。文档片段用作一个临时的占位
符,放置新创建的项目。然后使用 appendChild()将所有项目添加到列表中。记住,当给 appendChild()
传入文档片段时,只有片段中的子节点被添加到目标,片段本身不会被添加的。
一旦需要更新 DOM,请考虑使用文档片段来构建 DOM 结构,然后再将其添加到现存的文档中。